package com.springcloudalibaba.apiAsyn.configurer;

import com.springcloudalibaba.baserocketmq.service.RocketMqService;
import com.springcloudalibaba.bclass.base.BaseException;
import com.springcloudalibaba.bclass.base.BaseFeignException;
import com.springcloudalibaba.bclass.base.BaseLogEntity;
import com.springcloudalibaba.bclass.base.BaseResult;
import com.springcloudalibaba.bclass.enums.SysStateEnum;
import com.springcloudalibaba.bclass.enums.LogCategoryEnum;
import com.springcloudalibaba.bclass.enums.RocketMqDelayLevelEnum;
import com.springcloudalibaba.bclass.constant.RocketMqTopicConstant;
import com.springcloudalibaba.bclass.util.DateUtil;
import com.springcloudalibaba.bclass.util.LoggerUtil;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import java.text.MessageFormat;
import java.util.Date;

/**
 * @描述 TODO 自定义控制器异常捕获器
 * @作者 lixing
 * @日期 2019/6/29 22:48
 */
/**
 * 1、Spring Boot默认的异常处理机制：在请求到达controller之前发生错误，会发起 "error/状态码" 请求,所以只需要在template目录下创建error.html文件即可
 *   1.1、浏览器发出来的：返回html代码
 *   1.2、非浏览器发出来的：返回json
 *   详解org.springframework.boot.autoconfigure.web.BasicErrorController
 * 2、通过ControllerAdvice注解自定义全局Controller异常处理器来捕获Controller层抛出的异常
 *   2.1、Restful API请求（@ResponseBody、@RestController注解修饰的Controller）发生错误，输出JSON
 *   2.2、普通页面请求发生错误，输出错误页面
 */
@ControllerAdvice
public class ControllerExceptionHandler {
    @Resource
    private RocketMqService<BaseLogEntity> rocketMqService;

    /**
     * @描述 捕获所有类型的异常
     * @作者 lixing
     * @日期 2019/8/11 15:01
     * @Param [request, response, handlerMethod, e]
     * @return org.springframework.web.servlet.ModelAndView
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception e) {
        // 请求地址
        String requestUrl = request.getRequestURI();
        // 请求方法的类型 GET、POST
        String requestMethod = request.getMethod();
        // 服务器错误码 403、404、500
        String status = String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value());
        // 异常编码
        String code = String.valueOf(SysStateEnum.RESPONSE_STATUS_FALSE.getValue());
        String message = e.getMessage();
        if (e instanceof BaseException) {
            //
        } else if (e instanceof BaseFeignException) {
            message = message.split(":")[1];
        } else {
            LoggerUtil.error(MessageFormat.format(
                    "【全局异常控制器】==========\n服务器错误编码<{0}>：-[{1}]{2}\n自定义异常编码<{3}>：{4}",
                    status,
                    requestMethod,
                    requestUrl,
                    code,
                    e.fillInStackTrace()));
            try {
                BaseLogEntity baseLogEntity = new BaseLogEntity();
                // 日志类型
                baseLogEntity.setCategory(LogCategoryEnum.API_ASYN.getCategory());
                // 异常内容
                baseLogEntity.setContent(e.getMessage());
                // 创建时间
                baseLogEntity.setCreateTime(new Date());
                // TODO RocketMQ异步添加异常日志
                rocketMqService.sendAsyncMsg(RocketMqTopicConstant.LOG_TOPIC_API, baseLogEntity, 10000, RocketMqDelayLevelEnum.SECONDS_ONE.getLevel());
                // 自定义异常信息
                message = MessageFormat.format("系统错误[{0}]", DateUtil.dateToDateTimeCharacter(baseLogEntity.getCreateTime()));
            } catch (Exception exception) {
                LoggerUtil.error(MessageFormat.format("日志入库出错：{0} {1}", LogCategoryEnum.API_ASYN.getCategory(), exception.getMessage()));
            }
        }

        boolean isRestfulApi =
                handlerMethod != null &&
                        (handlerMethod.hasMethodAnnotation(ResponseBody.class) || handlerMethod.getBeanType().isAnnotationPresent(RestController.class));
        if (isRestfulApi) {
            BaseResult.outputJson(response, new BaseResult<String>().fail(message));
        } else {
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.addObject("code", code);
            modelAndView.addObject("msg", message);
            switch (status) {
                case "403":
                    modelAndView.setViewName("403");
                    break;
                case "404":
                    modelAndView.setViewName("404");
                    break;
                default:
                    modelAndView.setViewName("error");
                    break;
            }
            return modelAndView;
        }
        return null;
    }

}
